﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NYurik.TimeSeriesDb;
using NYurik.TimeSeriesDb.Common;
using System.Runtime.InteropServices;

namespace DataEngine.DataModel
{
    public struct ItemLngDbl
    {
        // ReSharper disable FieldCanBeMadeReadOnly.Global
        // ReSharper disable MemberCanBePrivate.Global
        [Index] public long SequenceNum;
        public double Value;
        // ReSharper restore MemberCanBePrivate.Global
        // ReSharper restore FieldCanBeMadeReadOnly.Global

        public ItemLngDbl(long sequenceNum, double value)
        {
            SequenceNum = sequenceNum;
            Value = value;
        }

        public override string ToString()
        {
           // return string.Format("{0,3}: {1}", SequenceNum, Value);
           return string.Format("{0,3}: {1}", SequenceNum, Value);
        }
    }
    public struct ItemULngDbl
    {
        // ReSharper disable FieldCanBeMadeReadOnly.Global
        // ReSharper disable MemberCanBePrivate.Global
        [Index]
        public ulong SequenceNum;
        public double Value;
        // ReSharper restore MemberCanBePrivate.Global
        // ReSharper restore FieldCanBeMadeReadOnly.Global

        public ItemULngDbl(ulong sequenceNum, double value)
        {
            SequenceNum = sequenceNum;
            Value = value;
        }

        public override string ToString()
        {
            // return string.Format("{0,3}: {1}", SequenceNum, Value);
            return string.Format("{0,3}: {1}", SequenceNum, Value);
        }
    }
    public struct ItemULngULng
    {
        // ReSharper disable FieldCanBeMadeReadOnly.Global
        // ReSharper disable MemberCanBePrivate.Global
        [Index]
        public ulong SequenceNum;
        public ulong Value;
        // ReSharper restore MemberCanBePrivate.Global
        // ReSharper restore FieldCanBeMadeReadOnly.Global

        public ItemULngULng(ulong sequenceNum, ulong value)
        {
            SequenceNum = sequenceNum;
            Value = value;
        }

        public override string ToString()
        {
            // return string.Format("{0,3}: {1}", SequenceNum, Value);
            return string.Format("{0,3}: {1}", SequenceNum, Value);
        }
    }
    public struct ItemULngByte
    {
        // ReSharper disable FieldCanBeMadeReadOnly.Global
        // ReSharper disable MemberCanBePrivate.Global
        [Index]
        public ulong SequenceNum;
        public byte Value;
        // ReSharper restore MemberCanBePrivate.Global
        // ReSharper restore FieldCanBeMadeReadOnly.Global

        public ItemULngByte(ulong sequenceNum, byte value)
        {
            SequenceNum = sequenceNum;
            Value = value;
        }

        public override string ToString()
        {
            // return string.Format("{0,3}: {1}", SequenceNum, Value);
            return string.Format("{0,3}: {1}", SequenceNum, Value);
        }
    }
    public struct ItemLngDblByte
    {
        // ReSharper disable FieldCanBeMadeReadOnly.Global
        // ReSharper disable MemberCanBePrivate.Global
        [Index]
        public long SequenceNum;
        public double Value;
        public byte Quality;
        // ReSharper restore MemberCanBePrivate.Global
        // ReSharper restore FieldCanBeMadeReadOnly.Global

        public ItemLngDblByte(long sequenceNum, double value,byte quality)
        {
            SequenceNum = sequenceNum;
            Value = value;
            Quality = quality;
        }

        public override string ToString()
        {
            // return string.Format("{0,3}: {1}", SequenceNum, Value);
            return string.Format("{0,3}: {1}", SequenceNum, Value);
        }
    }
    public struct ItemULngDblUShort
    {
        // ReSharper disable FieldCanBeMadeReadOnly.Global
        // ReSharper disable MemberCanBePrivate.Global
        [Index]
        public ulong SequenceNum;
        public double Value;
        public ushort DataType;
        // ReSharper restore MemberCanBePrivate.Global
        // ReSharper restore FieldCanBeMadeReadOnly.Global

        public ItemULngDblUShort(ulong sequenceNum, double value, byte datatype)
        {
            SequenceNum = sequenceNum;
            Value = value;
            DataType = datatype;
        }

        public override string ToString()
        {
            return string.Format("{0,3}: {1} {2}", SequenceNum, Value, DataType);
        }
    }
    public struct ItemLngDblDbl
    {
        // ReSharper disable FieldCanBeMadeReadOnly.Global
        // ReSharper disable MemberCanBePrivate.Global
        [Index] public long SequenceNum;
        public double Value1;
        public double Value2;
        // ReSharper restore MemberCanBePrivate.Global
        // ReSharper restore FieldCanBeMadeReadOnly.Global

        public ItemLngDblDbl(long sequenceNum, double value1, double value2)
        {
            SequenceNum = sequenceNum;
            Value1 = value1;
            Value2 = value2;
        }

        public override string ToString()
        {
            return string.Format("{0,3}: {1} {2}", SequenceNum, Value1, Value2);
        }
    }

    public static class DataEngineStream<TVal>
    {
        public static TVal ReadPoint(string filename, ulong timestamp)
        {
            //  Re-open the file for reading only (file can be opened for reading in parallel, but only one write)
            //  IWritableFeed<,> interface is better as it will work with non-compressed files as well
            using (var bf = (IWritableFeed<ulong, TVal>)BinaryFile.Open(filename, true))
            {
                return  bf.Stream(timestamp, inReverse: true, maxItemCount: 1).First();
            }
        }
        public static List<TVal> ReadList(string filename, ulong timestamp, uint duration)
        {
            //  Re-open the file for reading only (file can be opened for reading in parallel, but only one write)
            //  IWritableFeed<,> interface is better as it will work with non-compressed files as well
            using (var bf = (IWritableFeed<ulong, TVal>)BinaryFile.Open(filename, true))
            {
                ulong endTime = (ulong)(timestamp + duration);
                List<TVal> myValList=bf.Stream(timestamp, timestamp + duration).ToList<TVal>();
                return myValList;
            }
        }
        public static List<TVal> ReadTrend(string filename, ulong timestamp, uint duration, uint resolution)
        {
            //  Re-open the file for reading only (file can be opened for reading in parallel, but only one write)
            //  IWritableFeed<,> interface is better as it will work with non-compressed files as well
            using (var bf = (IWritableFeed<ulong, TVal>)BinaryFile.Open(filename, true))
            {
                List<TVal> myValList = new List<TVal>();
                uint count=duration/resolution;
                for (int i = 0; i < count; i++)
                {
                    ulong startTime = timestamp + (ulong)i * resolution;
                    myValList.Add(ReadPoint(filename, startTime));
                }
               
                return myValList;
            }
        }
        public static List<TVal> Read(string filename, ulong timestamp, uint duration = 0, uint resolution = 0)
        {
            if (resolution != 0 && duration!= 0 && timestamp !=0)
            {
                return ReadTrend(filename, timestamp, duration, resolution);
            }
            else if (duration != 0 && timestamp !=0)
            {
                return ReadList(filename, timestamp, duration);
            }

            return null;
        }

        public static TVal ReadPoint(IWritableFeed<ulong, TVal> bf, ulong timestamp)
        {
            //ulong myTimeStamp = bf.LastIndex;
            //if (myTimeStamp < timestamp) myTimeStamp = timestamp;
            //return bf.Stream(myTimeStamp, inReverse: true, maxItemCount: 1).First();

            if (timestamp < bf.FirstIndex) return default(TVal);
            if (timestamp == bf.FirstIndex)
            {
                return bf.Stream().First();
            }
            else
            {
                return bf.Stream(timestamp+1, maxItemCount: 1).Last();
                //return bf.Stream(timestamp, inReverse: true, maxItemCount: 1).First();
            }
        }
        public static List<TVal> ReadList(IWritableFeed<ulong, TVal> bf, ulong timestamp, uint duration)
        {
            ulong startTime = (ulong)(timestamp - duration);
            List<TVal> myValList = bf.Stream(startTime, timestamp+1).ToList<TVal>();
            return myValList;
        }
        public static List<TVal> ReadTrend(IWritableFeed<ulong, TVal> bf, ulong timestamp, uint duration, uint resolution, int Offset=1,bool LastFile=false)
        {
            List<TVal> myValList = new List<TVal>();

            ulong myStartTime = timestamp - duration;
            uint count = duration / resolution;
            // From timestamp back to 10 items only
            for (int i = Offset; i < count + 1; i++)
            {
                ulong readTime = myStartTime + (ulong)i * resolution;
                if (readTime > bf.LastIndex +resolution & !LastFile) {break;}
                myValList.Add( ReadPoint(bf, readTime));
            }

            return myValList;
        }
        public static List<TVal> Read(IWritableFeed<ulong, TVal> bf, ulong timestamp, uint duration = 0, uint resolution = 0)
        {
            if (resolution != 0 && duration != 0 && timestamp != 0)
            {
                return ReadTrend(bf, timestamp, duration, resolution);
            }
            else if (duration != 0 && timestamp != 0)
            {
                return ReadList(bf, timestamp, duration);
            }

            return null;
        }

        public static void Write(IWritableFeed<ulong, TVal> bf, List<TVal> myDataList)
        {
            IEnumerable<ArraySegment<TVal>> data = DataManager.MakeArraySegment(myDataList);
            bf.AppendData(data, true);
        }
    }
}
